/*
 *	main.cpp
 *
 *	wavein.exe
 *
 *	Audio Finger Print project
 */

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mmsystem.h>

#include "..\waveio\wave.h"

enum Error
{
	NoError,
	NoFile,
	NoMemory,
	UnknownError
};

static WAVE wave = 
{
	'FFIR', 0, 'EVAW', 
	' tmf', 16, 
	1, // AudioFormat
	1, // NumChannels
	8000, // SampleRate
	8000*1*2, // ByteRate
	1*2, // BlockAlign
	16, // BitsPerSample
	'atad', 0
};

static int framesize = 1024;

static HWAVEIN hWaveIn;

#define WAVEIN_BUFFERS	2
static volatile int sw;
static WAVEHDR PCMwh[WAVEIN_BUFFERS];
static LPVOID Buffer[WAVEIN_BUFFERS];

int WAVEIN_Open();
int WAVEIN_Record();
int WAVEIN_Stop();
static void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2);

int main(const int argc, const char* argv[])
{
	FILE* fout;
	int j = 0;
	int sw0 = 0;

	if(argc < 2)
	{
		printf(
			"not enough arguments\n"
			"usage: wavin output.wav [frame size]\n"
			);
		exit(1);
	}
	if(NULL == (fout = fopen(argv[1], "wb")))
	{
		perror("open file for write");
		exit(1);
	}
	if(1 != fwrite(&wave, sizeof(WAVE), 1, fout))
	{
		perror("can not write WAVE header");
		exit(1);
	}

	if(argc >= 3)
	{
		framesize = atoi(argv[2]);
	}

	if(WAVEIN_Open())
	{
		printf("waveInOpen error\n");
		exit(1);
	}

	sw = 0;

	WAVEIN_Record();

	for(j = 0;; ++j)
	{
		sw0 = sw;

		while(sw0 == sw)
		{
			Sleep(0);
		}

		if(framesize != fwrite(Buffer[sw0], sizeof(__int16), framesize, fout))
		{
			perror("can not write");
			break;
		}
		printf(".");

		if(_kbhit())
		{
			char c = _getch();
			printf("\nUser interrupt\n");
			break;
		}
	}

	WAVEIN_Stop();

	wave.SubChunk2Size = framesize*sizeof(__int16) * j;
	wave.ChunkSize = wave.SubChunk2Size + 36;
	fseek(fout, 0, SEEK_SET);
	fwrite(&wave, 1, sizeof(WAVE), fout);

	return 0;
}

void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
	if(WIM_DATA == uMsg)
	{
		WAVEIN_Record();
	}
}

int WAVEIN_Record()
{
	PCMwh[sw].lpData			= (char*)Buffer[sw];
	PCMwh[sw].dwBufferLength	= framesize*sizeof(__int16);

	if (waveInPrepareHeader(hWaveIn, &PCMwh[sw], sizeof(WAVEHDR)) == MMSYSERR_NOERROR)
	{
		waveInAddBuffer(hWaveIn, &PCMwh[sw], sizeof(WAVEHDR));
		waveInStart(hWaveIn);

		if(++sw >= WAVEIN_BUFFERS)
		{
			sw = 0;
		}

		return NoError;
	}

	return UnknownError;
}

int WAVEIN_Stop()
{
	waveInStop(hWaveIn);

	for(int i = 0; i < WAVEIN_BUFFERS; ++i)
	{ 
		waveInUnprepareHeader(hWaveIn, &PCMwh[i], sizeof(WAVEHDR));
	}

	waveInClose(hWaveIn);

	return NoError;
}

int WAVEIN_Open()
{
	WAVEFORMATEX format;

	format.cbSize = 0;
	format.wFormatTag = wave.AudioFormat;
	format.nChannels = wave.NumChannels;
	format.wBitsPerSample = wave.BitsPerSample;
	format.nSamplesPerSec = wave.SampleRate;
	format.nBlockAlign = wave.BlockAlign;
	format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;

	for(int i = 0; i < WAVEIN_BUFFERS; ++i)
	{
		if(NULL == (Buffer[i] = GlobalAlloc(GPTR,framesize*sizeof(__int16))))
		{
			printf("memory allocation error\n");
			exit(1);
		}
	}

#if 1
    UINT num = waveInGetNumDevs();
    WAVEINCAPS wic;
    for(UINT i = 0; i < num; ++i)
    {
        if(MMSYSERR_NOERROR == waveInGetDevCaps(i, &wic, sizeof(wic)))
        {
            printf("wave device %d: %s\n",i,wic.szPname);
        }
    }
    printf("Select wave device ");
    num = getchar()-'0';
    printf("Selected wave device %d\n", num);

	if(MMSYSERR_NOERROR == waveInOpen(&hWaveIn, num, &format, (DWORD_PTR)waveInProc, 0, CALLBACK_FUNCTION|WAVE_FORMAT_DIRECT))
#else
	if(MMSYSERR_NOERROR == waveInOpen(&hWaveIn, WAVE_MAPPER, &format, (DWORD_PTR)waveInProc, 0, CALLBACK_FUNCTION|WAVE_FORMAT_DIRECT))
#endif
	{
		return NoError;
	}
	else
	{
		return UnknownError;
	}
}
